# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.930.24.2 -> 1.930.24.3 # drivers/char/serial.c 1.33 -> 1.34 # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 03/02/13 bjorn_helgaas@hp.com 1.930.23.3 # ia64: Use has_8259 rather than initdata. # -------------------------------------------- # 03/02/13 bjorn_helgaas@hp.com 1.930.23.4 # ia64: Really remove ACPI SPCR parsing. # -------------------------------------------- # 03/02/13 hch@sgi.com 1.930.18.2 # [SPARC]: Add xattr syscalls. # -------------------------------------------- # 03/02/13 davem@nuts.ninka.net 1.930.17.10 # [NET]: Fix length in skb_padlen. # -------------------------------------------- # 03/02/13 fubar@us.ibm.com 1.930.17.11 # [BONDING]: Add MAINTAINERS entry. # -------------------------------------------- # 03/02/13 bjorn_helgaas@hp.com 1.930.23.5 # Cset exclude: eranian@frankl.hpl.hp.com[helgaas]|ChangeSet|20030103231109|26349 # -------------------------------------------- # 03/02/13 eranian@frankl.hpl.hp.com 1.930.23.6 # ia64: new perfmon patch for 2.4.20 # # Here is a new complete patch for perfmon against 2.4.20. Note that this # patch supersedes the one I sent you last week. This patch does: # # - fix the SMP system-wide monitoring problem # - add support for excluding idle tasks from system wide monitoring sessions # - regroup all __asm__ into a set of inline functions: cleaner and will make it # easier for the Secure Linux folks (Tom Cristian). # - optimize cost of psr fixup in system wide (simplify local_cpu_info). # - reestablish restriction of pfm_write_{pmds,pmcs} when passing the pid # of another process. We do not support this yet. # - update perfmon revision to 1.3 # -------------------------------------------- # 03/02/13 bjorn_helgaas@hp.com 1.930.23.7 # ia64: fix perfmon typo (PFM_CPU_SYST_WIDE should be PFM_CPUINFO_SYST_WIDE). # -------------------------------------------- # 03/02/13 bame@fc.hp.com 1.930.24.3 # HP multiport serial card fixes. # # Someday HP'll release boxes which can contain more than one # ECI/MP/Diva multiport serial card. This patch, unlike the earlier # one I sent, supports multiple instances of the card. # -------------------------------------------- # diff -Nru a/drivers/char/serial.c b/drivers/char/serial.c --- a/drivers/char/serial.c Wed Oct 8 09:05:43 2003 +++ b/drivers/char/serial.c Wed Oct 8 09:05:43 2003 @@ -257,6 +257,10 @@ static struct timer_list serial_timer; +#define HP_DIVA_CHECKTIME (1*HZ) +static struct timer_list hp_diva_timer; +static int hp_diva_count = 0; + /* serial subtype definitions */ #ifndef SERIAL_TYPE_NORMAL #define SERIAL_TYPE_NORMAL 1 @@ -793,6 +797,41 @@ } #ifdef CONFIG_SERIAL_SHARE_IRQ +static inline int is_hp_diva_info(struct async_struct *info) +{ + struct pci_dev *dev = info->state->dev; + return (dev && dev->vendor == PCI_VENDOR_ID_HP && + dev->device == PCI_DEVICE_ID_HP_SAS); +} + +static inline int is_hp_diva_irq(int irq) +{ + struct async_struct *info = IRQ_ports[irq]; + return (info && is_hp_diva_info(info)); +} + +/* + * It is possible to "use up" transmit empty interrupts in some + * cases with HP Diva cards. Figure out if there _should_ be a + * transmit interrupt and if so, return a suitable iir value so + * that we can recover when called from rs_timer(). + */ +static inline int hp_diva_iir(int irq, struct async_struct *info) +{ + int iir = serial_in(info, UART_IIR); + + if (is_hp_diva_info(info) && + (iir & UART_IIR_NO_INT) != 0 && + (info->IER & UART_IER_THRI) != 0 && + (info->xmit.head != info->xmit.tail || info->x_char) && + (serial_in(info, UART_LSR) & UART_LSR_THRE) != 0) { + iir &= ~(UART_IIR_ID | UART_IIR_NO_INT); + iir |= UART_IIR_THRI; + } + + return iir; +} + /* * This is the serial driver's generic interrupt routine */ @@ -823,7 +862,7 @@ do { if (!info->tty || - ((iir=serial_in(info, UART_IIR)) & UART_IIR_NO_INT)) { + ((iir=hp_diva_iir(irq, info)) & UART_IIR_NO_INT)) { if (!end_mark) end_mark = info; goto next; @@ -1092,9 +1131,11 @@ #ifdef CONFIG_SERIAL_SHARE_IRQ if (info->next_port) { do { - serial_out(info, UART_IER, 0); - info->IER |= UART_IER_THRI; - serial_out(info, UART_IER, info->IER); + if (!is_hp_diva_info(info)) { + serial_out(info, UART_IER, 0); + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } info = info->next_port; } while (info); #ifdef CONFIG_SERIAL_MULTIPORT @@ -1126,6 +1167,33 @@ } /* + * This subroutine is called when the hp_diva_timer goes off. In certain + * cases (multiple gettys in particular) Diva seems + * to issue only a single transmit empty interrupt instead of one each + * time THRI is enabled, causing interrupts to be "used up". This + * serves to poll the Diva UARTS more frequently than rs_timer() does. + */ +static void hp_diva_check(unsigned long dummy) +{ + static unsigned long last_strobe; + unsigned long flags; + int i; + + if (time_after_eq(jiffies, last_strobe + HP_DIVA_CHECKTIME)) { + for (i = 0; i < NR_IRQS; i++) { + if (is_hp_diva_irq(i)) { + save_flags(flags); cli(); + rs_interrupt(i, NULL, NULL); + restore_flags(flags); + } + } + } + last_strobe = jiffies; + mod_timer(&hp_diva_timer, jiffies + HP_DIVA_CHECKTIME); +} + + +/* * --------------------------------------------------------------- * Low level utility subroutines for the serial driver: routines to * figure out the appropriate timeout for an interrupt chain, routines @@ -4281,6 +4349,12 @@ break; } + if (hp_diva_count++ == 0) { + init_timer(&hp_diva_timer); + hp_diva_timer.function = hp_diva_check; + mod_timer(&hp_diva_timer, jiffies + HP_DIVA_CHECKTIME); + } + return 0; } @@ -5719,6 +5793,8 @@ /* printk("Unloading %s: version %s\n", serial_name, serial_version); */ del_timer_sync(&serial_timer); + if (hp_diva_count > 0) + del_timer_sync(&hp_diva_timer); save_flags(flags); cli(); remove_bh(SERIAL_BH); if ((e1 = tty_unregister_driver(&serial_driver)))